﻿Imports System.Collections.Concurrent
Imports Runa.Common.Helper
Imports Runa.Foundation.Messaging
Imports System.Collections

Namespace Threading

    Public NotInheritable Class ThreadPool

        Friend Shared Property ThreadPool() As New ConcurrentDictionary(Of Integer, Thread)

        Public Shared Function FromThread(ByVal tid As Integer) As Thread
            If ThreadPool.ContainsKey(tid) Then
                Return ThreadPool(tid)        
            Else
                If tid = GetCurrentThreadId() Then
                    Return New Thread(tid)
                Else
                    Throw New ArgumentNullException("Invalid thread id.")
                End If
            End If
        End Function

        Public Shared Function GetMessage(ByRef m As Runa.Foundation.Messaging.Message, ByVal tid As Integer) As Boolean
            Try
                m = Nothing
                Dim thr = Thread.FromThread(tid)
                While True
                    If thr.ThreadMessageQueue.Count = 0 Then
                        Continue While
                    Else
                        If thr.ThreadMessageQueue.TryDequeue(m) Then
                            Return True
                        Else
                            Throw New Exception("Failed to dequeue.")
                        End If
                    End If
                End While
                Return True
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function PeekMessage(ByRef m As Runa.Foundation.Messaging.Message, ByVal tid As Integer, ByVal removeMessage As Boolean) As Boolean
            Try
                m = Nothing
                Dim thr = Thread.FromThread(tid)
                If thr.ThreadMessageQueue.Count = 0 Then
                    Return False
                Else
                    If thr.ThreadMessageQueue.TryPeek(m) Then
                        If removeMessage Then
                            thr.ThreadMessageQueue.TryDequeue(m)
                        End If
                        Return True
                    Else
                        Return False
                    End If
                End If
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function GetMessage(ByRef m As Message, tid As Integer, [from] As MessageType, [to] As MessageType) As Boolean
            Try
                m = Nothing
                Dim thr = Thread.FromThread(tid)
                While True
                    If thr.ThreadMessageQueue.Count = 0 Then
                        Continue While
                    Else
                        While True
                            If thr.ThreadMessageQueue.TryDequeue(m) Then
                                If CInt(m.Message) >= CInt([from]) And CInt(m.Message) <= CInt([to]) Then
                                    Return True
                                End If
                            Else
                                Throw New Exception("Failed to dequeue.")
                            End If
                        End While
                    End If
                End While
                Return True
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function PeekMessage(ByRef m As Message, tid As Integer, from As MessageType, [to] As MessageType, removeMessage As Boolean) As Boolean
            Try
                m = Nothing
                Dim thr = Thread.FromThread(tid)
                If thr.ThreadMessageQueue.Count = 0 Then
                    Return False
                Else
                    While True
                        If thr.ThreadMessageQueue.TryPeek(m) Then
                            If removeMessage Then
                                thr.ThreadMessageQueue.TryDequeue(m)
                            End If
                            If CInt(m.Message) >= CInt([from]) And CInt(m.Message) <= CInt([to]) Then
                                Return True
                            End If
                        Else
                            Exit While
                        End If
                    End While
                End If
                Throw New Exception("Failed to peek.")
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function PostMessage(ByVal tid As Integer, ByVal m As Runa.Foundation.Messaging.Message) As Boolean
            Try
                Dim tsk = New Func(Of Integer, Message, Boolean)(AddressOf SendMessage)
                tsk.BeginInvoke(tid, m, Sub(ar) CType(ar.AsyncState, Func(Of Integer, Message, Boolean)).EndInvoke(ar), tsk)
                Return True
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function SendMessage(ByVal m As Runa.Foundation.Messaging.Message) As Boolean
            Dim result = True
            For Each item In ThreadPool
                result = result And SendMessage(item.Value.ThreadID, m)
            Next
            Return result
        End Function

        Public Shared Function SendMessage(ByVal tid As Integer, ByVal m As Runa.Foundation.Messaging.Message) As Boolean
            Try
                If tid = 0 Then Thread.CurrentThread.ThreadMessageQueue.Enqueue(m) : Return True
                For Each item In ThreadPool
                    Dim thr = item.Value
                    If tid > 0 Then
                        If thr.ThreadID = tid Then
                            thr.ThreadMessageQueue.Enqueue(m)
                        End If
                    Else
                        thr.ThreadMessageQueue.Enqueue(m)
                    End If
                Next
                Return True
            Catch ex As Exception
                Debug.WriteLine(ex.Message & ex.StackTrace.ToString())
                Return False
            End Try
        End Function

        Public Shared Function GetQueueStatus(flag As UInteger) As UInteger
            Dim thr = Thread.CurrentThread
            SyncLock thr.ThreadMessageQueue
                Return thr.ThreadMessageQueue.LongCount(
                    Function(item)
                        Dim result As Boolean = False
                        If CheckFlag(flag, QS_KEY) Then
                            result = result Or (item.Message >= MessageType.WM_KEYFIRST And item.Message <= MessageType.WM_KEYLAST)
                        End If
                        If CheckFlag(flag, QS_MOUSE) Then
                            result = result Or (item.Message >= MessageType.WM_MOUSEFIRST And item.Message <= MessageType.WM_MOUSELAST)
                        End If
                        If CheckFlag(flag, QS_TIMER) Then
                            result = result Or (item.Message = MessageType.WM_TIMER)
                        End If
                        Return result
                    End Function)
            End SyncLock
        End Function

    End Class

End Namespace